﻿using System;
using System.Collections.Generic;

namespace GLDotNet
{
    public class ShaderProgram : GLObject
    {        
        List<Shader> shaders;
        Dictionary<string, int> uniformLocations;
                
        public ShaderProgram(GraphicsDevice graphicsDevice)
            : base(graphicsDevice)
        {            
            this.Handle = this.Context.CreateProgram();

            this.shaders = new List<Shader>();
            this.uniformLocations = new Dictionary<string, int>();
        }

        protected override void DeleteObject()
        {
            foreach (Shader shader in this.shaders)
            {
                if (!shader.IsDisposed)
                    shader.Dispose();
            }

            this.shaders.Clear();
            this.shaders = null;

            this.Context.DeleteProgram(this.Handle);
        }

        public void AttachShader(Shader shader)
        {
            if (shader == null)
                throw new ArgumentNullException("shader");

            this.Context.AttachShader(this.Handle, shader.Handle);

            this.shaders.Add(shader);
        }

        public void DetachShader(Shader shader)
        {
            if (shader == null)
                throw new ArgumentNullException("shader");

            this.Context.DetachShader(this.Handle, shader.Handle);

            this.shaders.Remove(shader);
        }

        public void Link()
        {
            this.Context.LinkProgram(this.Handle);
        }

        public void Use()
        {
            this.Context.UseProgram(this.Handle);
        }

        public void BindAttribLocation(uint index, string name)
        {
            this.Context.BindAttribLocation(this.Handle, index, name);
        }

        public int GetUniformLocation(string name)
        {
            if (!this.uniformLocations.ContainsKey(name))
                this.uniformLocations[name] = this.Context.GetUniformLocation(this.Handle, name);

            return this.uniformLocations[name];
        }

        public void SetSampler(string name, uint textureUnit, Texture2D texture)
        {
            if (texture == null)
                throw new ArgumentNullException("texture");

            this.GraphicsDevice.SetActiveTexture(textureUnit);
            this.GraphicsDevice.BindTexture(TextureTarget.Texture2D, texture.Handle);

            this.Context.Uniform1(this.GetUniformLocation(name), textureUnit);
        }

        public void SetUniform(string name, int value)
        {
            this.Context.Uniform1(this.GetUniformLocation(name), value);
        }

        public void SetUniform(string name, float value)
        {
            this.Context.Uniform1(this.GetUniformLocation(name), value);
        }

        public void SetUniformMatrix(string name, ref Matrix4 matrix)
        {
            this.Context.UniformMatrix4(this.GetUniformLocation(name), ref matrix);
        }
    }
}
